package com.leyou.search.service.impl;

import com.leyou.item.bo.SpuBo;
import com.leyou.item.pojo.Category;
import com.leyou.item.pojo.SpecParam;
import com.leyou.item.pojo.Spu;
import com.leyou.search.client.BrandClient;
import com.leyou.search.client.CategoryClient;
import com.leyou.search.client.GoodsClient;
import com.leyou.search.client.SpecClient;
import com.leyou.search.pojo.Goods;
import com.leyou.search.repository.GoodsRepository;
import com.leyou.search.service.SearchService;
import com.leyou.search.utils.SearchRequest;
import com.leyou.search.utils.SearchResult;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.LongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.FetchSourceFilter;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import pojo.Brand;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Author: Saka
 * Date: 2019/2/13
 * Time: 20:23
 */
@Service
public class SearchServiceImpl implements SearchService {

    @Autowired
    private GoodsRepository goodsRepository;
    @Autowired
    private BrandClient brandClient;
    @Autowired
    private CategoryClient categoryClient;

    @Autowired
    private SpecClient specClient;

    @Autowired
    private GoodsClient goodsClient;

    @Override
    public SearchResult query(SearchRequest searchRequest) {
        String key = searchRequest.getKey();
        if (!StringUtils.isNotBlank(key)) {
        //如果用户没有搜索条件,我们可以给出默认的,或者返回null
            return null;
        }
        Integer page = searchRequest.getPage() - 1;//从0开始
        Integer size = searchRequest.getSize();
        //创建查询构建器
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        //查询
        //对结果进行筛选
        queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{"id", "skus", "subTitle"}, null));
        //基本信息
        queryBuilder.withQuery(QueryBuilders.matchQuery("all", key));
        //使用查询构造方法构造一个查询条件对象
        QueryBuilder query = buildBasicQueryWithFilter(searchRequest);
        //把查询条件对象加入到大的查询构造器中
        queryBuilder.withQuery(query);
        //分页
        queryBuilder.withPageable(PageRequest.of(page, size));
        //排序
        String sortBy = searchRequest.getSortBy();
        Boolean descending = searchRequest.getDescending();
        if (StringUtils.isNotBlank(sortBy)) {
            //如果不为空,则进行排序
            queryBuilder.withSort(SortBuilders.fieldSort(sortBy).order(descending ? SortOrder.DESC : SortOrder.ASC));
        }
        List<Category> categories = new ArrayList<>();

        List<Brand> brands = new ArrayList<>();

        //聚合
        String categoryAggName = "category";//商品分类聚合名称
        queryBuilder.addAggregation(AggregationBuilders.terms(categoryAggName).field("cid3"));
        String brandAggName = "brand";// 品牌聚合名称
        queryBuilder.addAggregation(AggregationBuilders.terms(brandAggName).field("brandId"));


        //返回结果
        AggregatedPage<Goods> search = (AggregatedPage<Goods>) this.goodsRepository.search(queryBuilder.build());

        //解析聚合
        LongTerms categoryTerms = (LongTerms) search.getAggregation(categoryAggName);
        List<LongTerms.Bucket> categoryBuckets = categoryTerms.getBuckets();
        //获取聚合到的分类id集合
        List<Long> cids = new ArrayList<>();
        for (LongTerms.Bucket categoryBucket : categoryBuckets) {
            cids.add(categoryBucket.getKeyAsNumber().longValue());
        }
        //根据分类的id查询分类名称
        List<String> names = this.categoryClient.queryNameByIds(cids);
        for (int i = 0; i < names.size(); i++) {
            Category c = new Category();
            c.setId(cids.get(i));
            c.setName(names.get(i));
            categories.add(c);
        }
        //获取聚合到的品牌id的集合
        LongTerms brandTerms = (LongTerms) search.getAggregation(brandAggName);
        List<LongTerms.Bucket> brandBuckets = brandTerms.getBuckets();
        List<Long> brandIds = new ArrayList<>();
        for (LongTerms.Bucket brandBucket : brandBuckets) {
            brandIds.add(brandBucket.getKeyAsNumber().longValue());
        }

        for (Long brandId : brandIds) {
            brands.add(this.brandClient.queryBrandById(brandId));
        }
        //解析结果
        long total = search.getTotalElements();
        long totalPage = (total + size - 1) / size;

        //所有的可搜索规格参数以及其对应的数组值
        List<Map<String, Object>> specs = null;
        //展现规格参数前提条件必须确定分类,只能是一个
        //判断商品分类数量,看是否需要对规格参数进行聚合
        if (null!=categories && categories.size()==1) {
            //如果分类只剩一个,才进行 规格参数过滤
            specs = getSpecs(categories.get(0).getId(),query);
        }
        return new SearchResult(total,totalPage, search.getContent(),categories,brands,specs);
    }
    //根据分类来处理可搜索的规格参数,对其进行动态聚合(规格参数的处理应该和查询相关,当查询条件变更后规格参数也应该随之变化)
    private List<Map<String, Object>> getSpecs(Long cid,QueryBuilder query) {

        List<Map<String, Object>> specs = new ArrayList<>();

        //获取当前分类所有的可搜索的规格参数
        List<SpecParam> specParams = this.specClient.querySpecParam(null, cid, null, true);

        //对这些可搜索的规格参数进行聚合
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();

        queryBuilder.withQuery(query);

        //循环添加聚合条件
        for (SpecParam specParam : specParams) {
            String aggName = specParam.getName();
            queryBuilder.addAggregation(AggregationBuilders.terms(aggName).field("specs." + aggName + ".keyword"));
        }
        //执行查询聚合
        AggregatedPage<Goods> search = (AggregatedPage<Goods>) this.goodsRepository.search(queryBuilder.build());

        //循环解析聚合
        for (SpecParam specParam : specParams) {
            String aggName = specParam.getName();
            StringTerms aggregation = (StringTerms) search.getAggregation(aggName);
            List<StringTerms.Bucket> buckets = aggregation.getBuckets();
            List<String> result = new ArrayList<>();
            for (StringTerms.Bucket bucket : buckets) {
                result.add(bucket.getKeyAsString());
            }

            //把结果动态的封装到map中,map有2个key
            Map<String, Object> spec = new HashMap<>();
            spec.put("k", aggName);
            spec.put("options", result);

            specs.add(spec);
        }
        return specs;
    }

    private QueryBuilder buildBasicQueryWithFilter(SearchRequest request) {
        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
        //基本查询条件
        queryBuilder.must(QueryBuilders.matchQuery("all", request.getKey()).operator(Operator.AND));
        //过滤条件构建器
        BoolQueryBuilder filterQueryBuilder = QueryBuilders.boolQuery();
        //整理过滤条件
        Map<String, String> filter = request.getFilter();
        for (Map.Entry<String, String> entry : filter.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            //商品分类和品牌已经聚合
            if ("cid3"!=key && "brandId"!=key) {
                key = "specs." + key + ".keyword";
            }
            //字符串类型,进行term查询
            filterQueryBuilder.must(QueryBuilders.termQuery(key, value));

        }
        //添加过滤条件
        queryBuilder.filter(filterQueryBuilder);
        return queryBuilder;

    }


}
